<?php
class AccessTokenModel extends RedisModel {
    const MEMCACHED_UID_HAS_ACCESSTOKEN = 'api:%uid%:has_accesstoken';
    const MEMCACHED_ACCESSTOKEN_KEY_NAME = 'api:accesstoken:%accesstoken%';

    const REDIS_UID_HAS_ACCESSTOKEN = 'oauth_api_[:uid]_has_accesstoken';
    const REDIS_ACCESSTOKEN_KEY_NAME = 'oauth_api_accesstoken_[:accesstoken]';

    function __construct(){
        parent::__construct();
    }

    private function crypto_rand_secure($min, $max) {
        $range = $max - $min;
        if ($range < 0) return $min; // not so random...
        $log = log($range, 2);
        $bytes = (int) ($log / 8) + 1; // length in bytes
        $bits = (int) $log + 1; // length in bits
        $filter = (int) (1 << $bits) - 1; // set all lower bits to 1
        do {
            $rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
            $rnd = $rnd & $filter; // discard irrelevant bits
        } while ($rnd >= $range);
        return $min + $rnd;
    }

    public function generateToken($length=32){
        $token = "";
        $codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        $codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
        $codeAlphabet.= "0123456789";
        for( $i = 0; $i<$length; $i++){
            $token .= $codeAlphabet[$this->crypto_rand_secure(0,strlen($codeAlphabet))];
        }
        return $token;
    }

    #获取uid has accesstoken的memcached key
    private function getUidRedisKey($uid = 0){
        return str_replace('%uid%', $uid, self::MEMCACHED_UID_HAS_ACCESSTOKEN);
    }

    #获取accesstoken has uid 的memcached key
    private function getRedisAccessTokenKey($accessToken = ''){
        return str_replace('%accesstoken%', $accessToken, self::MEMCACHED_ACCESSTOKEN_KEY_NAME);
    }


    public function getUidbyAccessToken($accessToken = ''){
        $Redis_key = $this->getRedisAccessTokenKey($accessToken);
        $uid = $this->getData($Redis_key);
        if(!empty($uid)){
            return $uid;
        }else{
            $user_model = new UserModel();
            $uid = $user_model->getUserInfoByAccessToken($accessToken);
            if(empty($uid)){
                return 0;
            }else{
                $this->catcheAccessToken($uid['uid'], $accessToken);
                return $uid;
            }
        }
    }

    #获取accesstoken
    public function getAccessTokenByUid($uid){
        $redis_key = $this->getUidRedisKey($uid);
        $accessToken = $this->getData($redis_key);
        if(empty($accessToken)){
            return '';
        }else{
            return $accessToken;
        }
    }

    #删除已经存在的accesstoken
    public function removeAccessToken($uid){
        $Redis_uid_key = $this->getUidRedisKey($uid);

        #删除memcache key
        $accessToken = $this->getData($Redis_uid_key);
        if(!empty($accessToken)){
            $RedisAccessTokenKey = $this->getRedisAccessTokenKey($accessToken);
            $this->delData($RedisAccessTokenKey);
            $this->delData($Redis_uid_key);
        }
    }

    #设置accesstoken
    public function setAccessToken($uid = 0){
        if((int)$uid == 0){
            $error_msg = "error_code:-20001";
            throw new Exception($error_msg);
            return False;
        }

        $redis_uid_has_accesstoken_key = $this->getUidRedisKey($uid);

        $accessToken = $this->generateToken();

        $redis_accesstoken_has_uid = $this->getRedisAccessTokenKey($accessToken);

        #删除已经存在的accesstoken信息
        $this->removeAccessToken($uid);

        $this->setData($redis_uid_has_accesstoken_key,$accessToken);

        #保存accesstoken has uid
        $this->setData($redis_accesstoken_has_uid,$uid);

        return $accessToken;
    }


    #清除缓存
    public function clearAccessToken($accessToken){
        $Redis_key = $this->getRedisAccessTokenKey($accessToken);
        $this->setData($Redis_key, 0, 1);
        return true;
    }

    #缓存token
    public function catcheAccessToken($uid = 0, $accessToken){
        if((int)$uid == 0 || empty($accessToken)){
            $error_msg = "error_code:-20001";
            throw new Exception($error_msg);
            return False;
        }

        $Redis_key = $this->getRedisAccessTokenKey($accessToken);
//
//        $redis_uid_has_accesstoken_key = $this->getUidRedisKey($uid);
//        $this->setData($redis_uid_has_accesstoken_key,$accessToken);
//
//        $redis_accesstoken_has_uid = $this->getRedisAccessTokenKey($accessToken);

        #保存accesstoken has uid
        $this->setData($Redis_key, $uid, 3600*24*7);

        return $accessToken;
    }

}